#include "Game.h"

IGame* createGame( HINSTANCE hInst, HWND hWnd )
{
	return new Game( hInst, hWnd );
}

Game::Game( HINSTANCE hInst, HWND hWnd ) :
	m_input( hInst, hWnd )
{
	// initialize camera
	m_camera.hand = Camera::RIGHTHANDED;

	m_camera.eye = float3( 0, 0, 50.f );
	m_camera.at  = float3( 0, 0, 0 );
	m_camera.up  = float3( 0, 1, 0 );

	m_camera.projectionType = Camera::PERSPECTIVE_FOV;
	m_camera.perspectiveFov.fovy = D3DXToRadian(45.f);
	m_camera.perspectiveFov.aspect = 1024.f/768.f;

	m_camera.zn = 1.0f;
	m_camera.zf = 1000.0f;

	// initialize polygon
	m_polygon.push_back( float3( cos(0.f), sin(0.f), 0 ) );
	m_polygon.push_back( float3( -0.5f, +sqrt(3.f)/2, 0 ) );
	m_polygon.push_back( float3( -0.5f, -sqrt(3.f)/2, 0 ) );

	// open the output file
	m_output.open( "output.txt" );
	if( m_output.fail() )
		throw;
	m_output << "STD\tmean\tlinear\trotational\n";

	// initialize rigid bodies
	m_bodies.resize( 200 );
	for( int i = 0; i < m_bodies.size(); i++ )
	{
		m_bodies[i].pos = float3( float(rand()%33-16), float(rand()%33-16), 0 );
		//m_bodies[i].invLocalInertia = zero33();
		float angle = (rand()%360)/360.f*TWO_PI;
		float3 unit = float3( cos(angle), sin(angle), 0 );

		if( m_bodies[i].pos[0] < 0 )
		{
			m_bodies[i].vel = unit;
		}
		else
		{
			m_bodies[i].vel = 6 * unit;
		}
	}
}

Game::~Game()
{
	m_polygonView.destroy();
	m_d3dDevice = 0;
	m_output.close();
}

void Game::getWindowSettings( int & width, int & height, bool & windowed, bool & min, bool & max )
{
	width = 640;
	height = 480;
	windowed = true;
}

void Game::onCreateGraphics( D3DDevice device )
{
	m_d3dDevice = device;
	m_polygonView.initialize( m_d3dDevice );
	m_polygonView.setup( m_polygon, float4( 1.0f, 1.0f, 1.0f, 1.0f ) );

	m_d3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

	float44 world = identity44();
	float44 view = m_camera.view();
	float44 proj = m_camera.projection();

	m_d3dDevice->SetTransform( D3DTS_WORLD, &world );
	m_d3dDevice->SetTransform( D3DTS_VIEW, &view );
	m_d3dDevice->SetTransform( D3DTS_PROJECTION, &proj );
}

void Game::onResetGraphics( D3DDevice device )
{
	//TODO: Add code to handle lost device ( resizing issue )

	m_d3dDevice = device;

	m_polygonView.destroy();
	m_polygonView.initialize( m_d3dDevice );
	m_polygonView.setup( m_polygon, float4( 1.0f, 1.0f, 1.0f, 1.0f ) );

	m_d3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

	float44 view = m_camera.view();
	float44 proj = m_camera.projection();

	m_d3dDevice->SetTransform( D3DTS_VIEW, &view );
	m_d3dDevice->SetTransform( D3DTS_PROJECTION, &proj );
}

void Game::onLostGraphics()
{
}

void Game::onFrameMove(float dt)
{
	dt = 1/100.f;
	for( int i = 0; i < m_bodies.size(); i++ )
	{
		poly::Polygon polyi = m_polygon.transform( m_bodies[i].world() );
		for( int j = i; j < m_bodies.size(); j++ )
		{
			// detect if bodies cannot collide before going into costly
				// computations
			if( lengthSq(m_bodies[i].pos - m_bodies[j].pos) > 4.f )
				continue;

			poly::Polygon polyj = m_polygon.transform( m_bodies[j].world() );
			
			Contact contact;
			if( intersect( contact, polyi, polyj ) )
			{
				// resolve collision in position space
				m_bodies[i].displace( -contact.seperation / 2 );
				m_bodies[j].displace( +contact.seperation / 2 );

				// resolve collision in velocity space
				m_bodies[i].resolveCollision( m_bodies[j], contact );
			}
		}

		// collide with box boundaries
		if( m_bodies[i].pos[0] > +16 && m_bodies[i].vel[0] > 0 )
			m_bodies[i].vel[0] = -m_bodies[i].vel[0];
		if( m_bodies[i].pos[0] < -16 && m_bodies[i].vel[0] < 0 )
			m_bodies[i].vel[0] = -m_bodies[i].vel[0];
		if( m_bodies[i].pos[1] > +16 && m_bodies[i].vel[1] > 0 )
			m_bodies[i].vel[1] = -m_bodies[i].vel[1];
		if( m_bodies[i].pos[1] < -16 && m_bodies[i].vel[1] < 0 )
			m_bodies[i].vel[1] = -m_bodies[i].vel[1];

		m_bodies[i].step(dt);
	}
	for( int i = 0; i < m_bodies.size(); i++ )
		m_bodies[i].resetForces();

	// statistical data
	double sum = 0;
	double sum_linear = 0;
	double sum_rotational = 0;

	for( int i = 0; i < m_bodies.size(); i++ )
	{
		double linear = lengthSq(m_bodies[i].vel);
		double rotational = lengthSq(m_bodies[i].rotVel);
		sum_linear += linear;
		sum_rotational += rotational;
	}
	sum = sum_linear + sum_rotational;
	double mean = sum / m_bodies.size();
	double mean_linear = sum_linear / m_bodies.size();
	double mean_rotational = sum_rotational / m_bodies.size();
	double variance = 0;
	for( int i = 0; i < m_bodies.size(); i++ )
	{
		double diff = lengthSq(m_bodies[i].vel)+lengthSq(m_bodies[i].rotVel) - mean;
		variance += diff*diff;
	}
	m_output
		<< sqrt(variance) << "\t"
		<< mean << "\t"
		<< mean_linear << "\t"
		<< mean_rotational << std::endl;
}

void Game::onFrameRender(float dt)
{
	for( int i = 0; i < m_bodies.size(); i++ )
	{
		float44 world = m_bodies[i].world();
		float lsq = ( lengthSq(m_bodies[i].vel)+
			lengthSq(m_bodies[i].rotVel) )
			/ 50.f;
		float red = lsq;
		if( red > 1.f ) red = 1.f;
		float green = 0;
		float blue = 0;
		if( lsq > 1.f ) green = lsq - 1;
		if( green > 1.f ) green = 1.f;
		if( lsq > 2.f ) blue = lsq - 2;
		if( blue > 1.f ) blue = 1.f;
		m_polygonView.setup( m_polygon,
			float4(1.f, red, green, blue)
			);
		m_polygonView.render( world );
	}
}

void Game::onDestroyGraphics()
{
}
